//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2007
//
// Transformation helper.
//

# include "StdAfx.h"
# include "TransformRect.h"
# include "PtAlgo.h"
# include "Transform.h"

class CProjectedCurve {
  public:
	CProjectedCurve (
		const point_t & _a, const point_t & _b,
		const C2WayTransformation * _pTransform1, const C2WayTransformation * _pTransform2,
		bool _bXOrY, bool _bReverse = false
	) :
		m_a (_a),
		m_b (_b),
		m_pTransform1 (_pTransform1),
		m_pTransform2 (_pTransform2),
		m_bXOrY (_bXOrY),
		m_bReverse (_bReverse)
	{}

	double operator () (double _t) {
		point_t p (
			m_a.x + _t*(m_b.x - m_a.x),
			m_a.y + _t*(m_b.y - m_a.y)
		);

		if (m_bReverse) {
			if (m_pTransform2)
				m_pTransform2->Reverse (p);

			if (m_pTransform1)
				m_pTransform1->Reverse (p);
		} else {
			if (m_pTransform1)
				m_pTransform1->ApplyTo (p);

			if (m_pTransform2)
				m_pTransform2->ApplyTo (p);
		}

		return m_bXOrY ? p.x : p.y;
	}

	double MinDef () const {return 0;}
	double MaxDef () const {return 1;}

  private:
	const point_t & m_a;
	const point_t & m_b;
	const C2WayTransformation * m_pTransform1;
	const C2WayTransformation * m_pTransform2;
	const bool m_bXOrY;
	const bool m_bReverse;
};

class CDumbOptimize {
  public:
	CDumbOptimize (CProjectedCurve & _f) :
		m_f (_f)
	{
		_Solve ();
	}

	point_t GetMin () const {return point_t (m_tMin, m_f (m_tMin));}
	point_t GetMax () const {return point_t (m_tMax, m_f (m_tMax));}

  private:
	CProjectedCurve & m_f;
	double m_tMin, m_tMax;

	void _Solve ();
};

void CDumbOptimize::_Solve () {
	const double tMin = m_f.MinDef ();
	const double tMax = m_f.MaxDef ();

	m_tMin = tMax;
	m_tMax = tMax;

	const double fStep = 1e-3*(tMax - tMin);
	double fMin = m_f (m_tMin);
	double fMax = fMin;

	for (double t = tMin; t < tMax; t += fStep) {
		const double v = m_f (t);

		if (v < fMin) {
			fMin = v;
			m_tMin = t;
		} else if (v > fMax) {
			fMax = v;
			m_tMax = t;
		}
	}
}

void TransformRect (
	const C2WayTransformation * _pTransform1, const C2WayTransformation * _pTransform2,
	const rect_t & _rectFrom, rect_t & _rectTo,
	bool _bReverse
) {
	const point_t pt00 (_rectFrom.x0, _rectFrom.y0);
	const point_t pt10 (_rectFrom.x1, _rectFrom.y0);
	const point_t pt01 (_rectFrom.x0, _rectFrom.y1);
	const point_t pt11 (_rectFrom.x1, _rectFrom.y1);

	CProjectedCurve pc1 (pt00, pt10, _pTransform1, _pTransform2, false, _bReverse);
	CProjectedCurve pc2 (pt01, pt11, _pTransform1, _pTransform2, false, _bReverse);
	CDumbOptimize opt1 (pc1);
	CDumbOptimize opt2 (pc2);

	_rectTo.y0 = __min (opt1.GetMin ().y, opt2.GetMin ().y);
	_rectTo.y1 = __max (opt1.GetMax ().y, opt2.GetMax ().y);

	CProjectedCurve pc3 (pt10, pt11, _pTransform1, _pTransform2, true, _bReverse);
	CProjectedCurve pc4 (pt00, pt01, _pTransform1, _pTransform2, true, _bReverse);
	CDumbOptimize opt3 (pc3);
	CDumbOptimize opt4 (pc4);

	_rectTo.x0 = __min (opt3.GetMin ().y, opt4.GetMin ().y);
	_rectTo.x1 = __max (opt3.GetMax ().y, opt4.GetMax ().y);
}

void TransformRectRough (
	const C2WayTransformation * _pTransform1, const C2WayTransformation * _pTransform2,
	const rect_t & _rectFrom, rect_t & _rectTo,
	bool _bReverse
) {
	point_t pt00 (_rectFrom.x0, _rectFrom.y0);
	point_t pt11 (_rectFrom.x1, _rectFrom.y1);

	if (_bReverse) {
		if (_pTransform2) {
			_pTransform2->Reverse (pt00);
			_pTransform2->Reverse (pt11);
		}

		if (_pTransform1) {
			_pTransform1->Reverse (pt00);
			_pTransform1->Reverse (pt11);
		}
	} else {
		if (_pTransform1) {
			_pTransform1->ApplyTo (pt00);
			_pTransform1->ApplyTo (pt11);
		}

		if (_pTransform2) {
			_pTransform2->ApplyTo (pt00);
			_pTransform2->ApplyTo (pt11);
		}
	}

	_rectTo.x0 = pt00.x;
	_rectTo.y0 = pt00.y;
	_rectTo.x1 = pt11.x;
	_rectTo.y1 = pt11.y;
}
